#include <string.h>
#include <stdint.h>
#include <stdlib.h>
#include <stdio.h>

#include <psp2/appmgr.h>

#include "boot.h"
#include "utils.h"
#include "file.h"
#include "config.h"
#include "browser.h"
#include "states.h"
#include "retro.h"

#define EXEC_BOOT_MODE_CASE_STR "boot_mode"
#define BOOT_GAME_PATH_CASE_STR "game_path"
#define PRIVATE_ASSETS_CASE_STR "core_assets_dir"
#define PUBLIC_ASSETS_CASE_STR "common_assets_dir"
#define RESTORE_APP_PATH_CASE_STR "restore_app_path"

int exec_boot_mode = 0;
static char *boot_game_path = NULL;
static char *restore_app_path = NULL;

static int bootparam_argc = 0;
static char **bootparam_argv = NULL;

int loadBootGame()
{
    if (!boot_game_path)
        return -1;

    int ret;
    changeToDirectoryFromPath(boot_game_path);
    if (!loadGame(boot_game_path))
        return -1;
    if (misc_config.auto_save_load)
        loadState(-1);

    return 0;
}

int readBootParamString(const char *str)
{
    char *name = NULL;
    char *string = NULL;
    int ret = configReadLine(str, &name, &string);
    if (ret <= 0)
        return ret;

    if (strcasecmp(name, EXEC_BOOT_MODE_CASE_STR) == 0)
        exec_boot_mode = configGetDecimal(string);
    else if (strcasecmp(name, BOOT_GAME_PATH_CASE_STR) == 0)
        boot_game_path = configGetString(string);
    else if (strcasecmp(name, PRIVATE_ASSETS_CASE_STR) == 0)
        private_assets_dir = configGetString(string);
    else if (strcasecmp(name, PUBLIC_ASSETS_CASE_STR) == 0)
        public_assets_dir = configGetString(string);
    else if (strcasecmp(name, RESTORE_APP_PATH_CASE_STR) == 0)
        restore_app_path = configGetString(string);

    if (name)
        free(name);
    if (string)
        free(string);

    return 1;
}

int initBootParams(int argc, char *const argv[])
{
    if (argc < 2)
        return 0;

    APP_LOG("argc: %d\n", argc);
    bootparam_argc = argc;
    bootparam_argv = (char **)calloc(argc, sizeof(char *));

    int i, len;
    for (i = 1; i < argc; i++)
    {
        len = strlen(argv[i]);
        bootparam_argv[i - 1] = (char *)malloc(len + 1);
        strcpy(bootparam_argv[i - 1], argv[i]);
        readBootParamString(argv[i]);
        APP_LOG("argv[%d]: %s\n", i, argv[i]);
    }
    bootparam_argv[bootparam_argc - 1] = NULL;

    return 0;
}

int restoreBootParams()
{
    if (exec_boot_mode <= 0)
        return -1;

    char *app_path;
    if (restore_app_path)
        app_path = restore_app_path;
    else
        app_path = "app0:eboot.bin";
    APP_LOG("restore_app_path: %s\n", app_path);

    int ret = sendBootParams(app_path, bootparam_argv);

    return ret;
}

int sendBootParams(char *app_path, char *argv[])
{
    int ret = sceAppMgrLoadExec(app_path, (char *const *)argv, NULL);

    return ret;
}

int loadGameWithBootParams(char *app_path, char *game_path, char *software)
{
    if (!checkFileExist(app_path))
        return -1;

    char boot_mode_param[MAX_CONFIG_LINE_LENGTH];
    snprintf(boot_mode_param, MAX_CONFIG_LINE_LENGTH, "%s=%d", EXEC_BOOT_MODE_CASE_STR, BOOT_MODE_GAME);

    char game_path_param[MAX_CONFIG_LINE_LENGTH];
    snprintf(game_path_param, MAX_CONFIG_LINE_LENGTH, "%s=\"%s\"", BOOT_GAME_PATH_CASE_STR, game_path);

    char private_assets_param[MAX_CONFIG_LINE_LENGTH];
    snprintf(private_assets_param, MAX_CONFIG_LINE_LENGTH, "%s=\"%s/%s\"", PRIVATE_ASSETS_CASE_STR, CORE_DATA_DIR, software);

    char public_assets_param[MAX_CONFIG_LINE_LENGTH];
    snprintf(public_assets_param, MAX_CONFIG_LINE_LENGTH, "%s=\"%s\"", PUBLIC_ASSETS_CASE_STR, APP_ASSETS_DIR);

    char *argv[] = {
        boot_mode_param,
        game_path_param,
        private_assets_param,
        public_assets_param,
        NULL,
    };
    int ret = sendBootParams(app_path, argv);

    return ret;
}

int loadCoreWithBootParams(char *app_path, char *software)
{
    if (!checkFileExist(app_path))
        return -1;

    char boot_mode_param[MAX_CONFIG_LINE_LENGTH];
    snprintf(boot_mode_param, MAX_CONFIG_LINE_LENGTH, "%s=%d", EXEC_BOOT_MODE_CASE_STR, BOOT_MODE_ARCH);

    char private_assets_param[MAX_CONFIG_LINE_LENGTH];
    snprintf(private_assets_param, MAX_CONFIG_LINE_LENGTH, "%s=\"%s/%s\"", PRIVATE_ASSETS_CASE_STR, CORE_DATA_DIR, software);

    char public_assets_param[MAX_CONFIG_LINE_LENGTH];
    snprintf(public_assets_param, MAX_CONFIG_LINE_LENGTH, "%s=\"%s\"", PUBLIC_ASSETS_CASE_STR, APP_ASSETS_DIR);

    char *argv[] = {
        boot_mode_param,
        private_assets_param,
        public_assets_param,
        NULL,
    };
    int ret = sendBootParams(app_path, argv);

    return ret;
}